#ifndef TOOLS_H_
#define TOOLS_H_

#ifdef _WIN64
#    define WiNAPI_FAMILY_PARTITION
#    include <Windows.h>
#    include <consoleapi.h>
#    include <processenv.h>
#    include <winnt.h>

#endif


#include <algorithm>
#include <chrono>
#include <cstddef>
#include <cstdio>
#include <cstring>
#include <random>
#include <ratio>
#include <string>
#include <type_traits>
#include <vector>


namespace lxj
{
class Time {
    using time_point = std::chrono::time_point<std::chrono::steady_clock>;
    using duration   = std::chrono::duration<double, std::milli>;

    time_point start;

public:
    Time() : start(std::chrono::steady_clock::now()) {}
    ~Time()
    {
        duration t = std::chrono::steady_clock::now() - start;
        std::printf("Time:----%fms", t.count());
    }
    Time(const Time&) = delete;
    Time(Time&&)      = delete;
};

template<class T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0>
class Random {
    std::mt19937 gen;

    Random() = default;

public:
    ~Random() {}
    Random(const Random&) = delete;
    Random(Random&&)      = delete;

    static Random& instance()
    {
        static Random      r;
        std::random_device rd;
        r.gen.seed(rd());
        return r;
    }

    inline T operator()(T low, T high)
    {
        if constexpr (std::is_floating_point_v<T>) {
            std::uniform_real_distribution<T> dis(low, high);
            return dis(gen);
        }
        else {
            std::uniform_int_distribution<T> dis(low, high);
            return dis(gen);
        }
        // return T(1) << (sizeof(T) * 8 - 1);
    }
};

#ifdef _WIN64
class OpenANSI {
    HANDLE hOut;
    DWORD  dwMode;

    std::string _mode  = "\033[0m";
    std::string _color = "\033[38;5;255m";

private:
    OpenANSI() = default;

public:
    OpenANSI(const OpenANSI&) = delete;

    ~OpenANSI() { std::fputs("\033[0m\n", stdout); }

    static OpenANSI& instance()
    {
        static OpenANSI instance;
        instance.hOut   = GetStdHandle(STD_OUTPUT_HANDLE);
        instance.dwMode = 0;
        GetConsoleMode(instance.hOut, &instance.dwMode);
        instance.dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
        SetConsoleMode(instance.hOut, instance.dwMode);
        std::fputs("\033[0m", stdout);

        return instance;
    }

    void print(const char* str, const char c = '\0')
    {
        if (c == '\0') {
            std::fputs(str, stdout);
            return;
        }

        this->set_mode(0);
        std::string                  s(str, strlen(str));
        auto                         count = std::count(s.begin(), s.end(), c);
        std::vector<decltype(count)> index;
        index.reserve(count);
        for (decltype(count) i = 0; i < s.size(); i++) {
            s[i] == c ? index.emplace_back(i) : (long long)0;
        }
        constexpr unsigned short mode_count  = 4;
        constexpr unsigned short end_mark    = 4;
        unsigned short           color_count = _color.size();

        size_t new_size = count * (mode_count + color_count + end_mark) + s.size();
        s.reserve(new_size);
        for (size_t i = 0; i < index.size(); i++) {
            size_t pos = index[i] + (mode_count + color_count + end_mark) * i;
            s.insert(pos + 1, "\033[0m");
            s.insert(pos, _mode + _color);
        }
        std::puts(s.c_str());
    }

    // 为字符设置模式
    /*
    0：重置（消除）所有颜色和图形
    粗体----开      关
            1       22
    虚化----开      关
            2       22
    斜体----开      关
            3       23
    下划线--开      关
            4       24
    闪烁----开      关
            5       25
    反向----开      关
            7       27
    隐形----开      关
            8       28
    删除线--开      关
            9       29
    */
    OpenANSI& set_mode(unsigned short mode)
    {
        mode = mode < 0 ? 0 : mode;
        mode = mode > 9 ? 9 : mode;

        char c_mode = '0' + mode;
        _mode[2]    = c_mode;
        std::fputs(_mode.c_str(), stdout);
        return *this;
    }

    // 为字符设置颜色
    /*0~255*/
    OpenANSI& set_color(unsigned short color)
    {
        color = color < 0 ? 0 : color;
        color = color > 255 ? 255 : color;

        std::string c_color = std::to_string(color);
        c_color += 'm';
        _color.replace(_color.begin() + 7, _color.end(), c_color);
        std::fputs(_color.c_str(), stdout);
        return *this;
    }

    OpenANSI& close()
    {
        set_mode(0);
        return *this;
    }
};
#endif

}   // namespace lxj

#endif